package org.groovyflow.spring.web;

import groovy.lang.Binding;
import groovy.text.SimpleTemplateEngine;
import groovy.text.Template;
import groovy.text.TemplateEngine;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Writer;
import java.util.Date;
import java.util.Map;
import java.util.WeakHashMap;

//Grabbed from TemplateServlet.
public class PartialsBuilder {
	
	   /* Simple file name to template cache map.
	     */
	    private final Map cache = new WeakHashMap();

	    /**
	     * Underlying template engine used to evaluate template source files.
	     */
	    private TemplateEngine engine = new SimpleTemplateEngine();
	    
	    private String path = "";
	    private String suffix = "";

	   
	    protected Template getTemplate(File file)  {

	        String key = file.getAbsolutePath();
	        Template template = null;

	        /*
	         * Test cache for a valid template bound to the key.
	         */
	        
	        TemplateCacheEntry entry = (TemplateCacheEntry) cache.get(key);
	        if (entry != null) {
	            if (entry.validate(file)) {
	                template = entry.template;
	            } 
	        } 

	        //
	        // Template not cached or the source file changed - compile new template!
	        //
	        if (template == null) {
	            FileReader reader = null;
	            try {
	                reader = new FileReader(file);
	                template = engine.createTemplate(reader);
	            } catch (Exception e) {
	                throw new RuntimeException("Creation of template failed: " + e, e);
	            } finally {
	                if (reader != null) {
	                    try {
	                        reader.close();
	                    } catch (IOException ignore) {
	                        // e.printStackTrace();
	                    }
	                }
	            }
	            cache.put(key, new TemplateCacheEntry(file, template, false));
	            
	        }

	        //
	        // Last sanity check.
	        //
	        if (template == null) {
	            throw new RuntimeException("Template is null? Should not happen here!");
	        }

	        return template;

	    }
	
	
	    protected File getScriptUriAsFile(String uri) {
	        String real = ServletContextHolder.servletContext.getRealPath(uri);
	        File file = new File(real).getAbsoluteFile();
	        return file;
	    }	
	    
	    public String  make(String uri, Binding binding){
	    	uri = this.path + uri + this.suffix;
	    	File file = getScriptUriAsFile(uri);
	    	Template template = getTemplate(file);
	    	try{
	    		return template.make(binding.getVariables()).toString();
	    	}catch(Exception ex){
	    		ex.printStackTrace();
	    		throw new RuntimeException(ex);
	    	}
	    }


		public void setEngine(TemplateEngine engine) {
			this.engine = engine;
		}		
		

		public void setPath(String path) {
			this.path = path;
		}	
		
		public void setSuffix(String suffix) {
			this.suffix = suffix;
		}		
	
	
	 /**
     * Simple cache entry that validates against last modified and length
     * attributes of the specified file. 
     *
     * @author Christian Stein
     */
    private static class TemplateCacheEntry {

        Date date;
        long hit;
        long lastModified;
        long length;
        Template template;

        public TemplateCacheEntry(File file, Template template) {
            this(file, template, false); // don't get time millis for sake of speed
        }

        public TemplateCacheEntry(File file, Template template, boolean timestamp) {
            if (file == null) {
                throw new NullPointerException("file");
            }
            if (template == null) {
                throw new NullPointerException("template");
            }
            if (timestamp) {
                this.date = new Date(System.currentTimeMillis());
            } else {
                this.date = null;
            }
            this.hit = 0;
            this.lastModified = file.lastModified();
            this.length = file.length();
            this.template = template;
        }

        /**
         * Checks the passed file attributes against those cached ones. 
         *
         * @param file
         *  Other file handle to compare to the cached values.
         * @return <code>true</code> if all measured values match, else <code>false</code>
         */
        public boolean validate(File file) {
            if (file == null) {
                throw new NullPointerException("file");
            }
            if (file.lastModified() != this.lastModified) {
                return false;
            }
            if (file.length() != this.length) {
                return false;
            }
            hit++;
            return true;
        }

        public String toString() {
            if (date == null) {
                return "Hit #" + hit;
            }
            return "Hit #" + hit + " since " + date;
        }

    }



}
